/* $ZEL: sis1100_irq_thread.c,v 1.10 2006/02/14 19:51:20 wuestner Exp $ */

/*
 * Copyright (c) 2002-2004
 * 	Peter Wuestner.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "sis1100_sc.h"

/*
 * this is a kernel thread which sleeps on sc->handler_wait;
 * it is waked up by doorbell irq,  irq_synch_chg, irq_lemo_in_chg
 * and link_up_timer;
 * it is responsable to inform the user process about VME interrupts,
 * CAMAC LAMs, interrupts caused by front panel IO and insertion or removal
 * of the optical link
 */

#if !defined(__NetBSD__) && ! defined(__linux__)
#error Wrong Operating System
#endif

#define IRQTHREAD_BRAKE 1000
#undef IRQTHREAD_BRAKE

static void
_sis1100_irq_thread(struct sis1100_softc* sc, enum handlercomm command)
{
    struct list_head* curr;
    /*pERROR(sc, "_sis1100_irq_thread: command=0x%x", command);*/

    SEM_LOCK(sc->sem_irqinfo);
    if (command&handlercomm_doorbell) {
        switch (sc->remote_hw) {
        case sis1100_hw_vme:
            sis3100rem_irq_handler(sc);
            break;
        case sis1100_hw_camac:
            sis5100rem_irq_handler(sc);
            break;
        case sis1100_hw_pci:
            /* do nothing */
            break;
        case sis1100_hw_lvd:
            zellvd_rem_irq_handler(sc);
            break;
        case sis1100_hw_invalid:
            /* do nothing */
            break;
        }
    }

    if (command&handlercomm_lemo) {
        sis1100_lemo_handler(sc);
    }

    if (command&handlercomm_mbx0) {
        sis1100_mbox0_handler(sc);
    }

    if (command&handlercomm_up) {
        sis1100_synch_handler(sc);
    }

    if (command&handlercomm_ddma) {
        sis1100_ddma_handler(sc);
    }

    list_for_each(curr, &sc->fdata_list_head) {
        struct sis1100_fdata* fd;
        fd=list_entry(curr, struct sis1100_fdata, list);
        if (fd->sig && (fd->sig!=-1) &&
                ((sc->new_irqs & fd->owned_irqs)||
                        (sc->old_remote_hw!=sc->remote_hw))) {
#ifdef __linux__
            int res;
            /*res=kill_proc_info(fd->sig, (void*)0, fd->pid);*/
            res=kill_proc(fd->pid, fd->sig, 0);
            if (res)
                pINFO(sc, "send sig %d to %d: res=%d", fd->sig, fd->pid, res);
#elif __NetBSD__
            psignal(fd->p, fd->sig);
#endif
        }
    }
    SEM_UNLOCK(sc->sem_irqinfo);

#ifdef __NetBSD__
    wakeup(&sc->remoteirq_wait);
    selwakeup(&sc->sel);
#elif __linux__
    wake_up_interruptible(&sc->remoteirq_wait);
#endif
}

#ifdef __linux__
int
#else
void
#endif
sis1100_irq_thread(void* data)
{
    struct sis1100_softc* sc=(struct sis1100_softc*)data;
    enum handlercomm command;
    DECLARE_SPINLOCKFLAGS(flags);
#ifdef IRQTHREAD_BRAKE
    static int brake=IRQTHREAD_BRAKE;
#endif

    pINFO(sc, "sis1100_irq_thread started");

#ifdef __linux__
#if LINUX_VERSION_CODE < 0x20600
    daemonize();
    snprintf(current->comm, sizeof(current->comm), "sis1100_%02d", sc->unit);
    SPIN_LOCK_IRQSAVE(current->sigmask_lock, flags);
    sigemptyset(&current->blocked);
    recalc_sigpending(current);
    SPIN_UNLOCK_IRQRESTORE(current->sigmask_lock, flags);
#else
    daemonize("sis1100_%02d", sc->unit);
    SPIN_LOCK_IRQSAVE(current->sighand->siglock, flags);
    sigemptyset(&current->blocked);
    recalc_sigpending();
    SPIN_UNLOCK_IRQRESTORE(current->sighand->siglock, flags);
#endif
#endif /*__linux__*/

    while (1) {
#ifdef __NetBSD__
        tsleep(&sc->handler_wait, PCATCH, "thread_vmeirq", 0);
#elif __linux__
        wait_event_interruptible(sc->handler_wait, sc->handlercommand.command);
#endif
        SPIN_LOCK_IRQSAVE(sc->handlercommand.lock, flags);
        command=sc->handlercommand.command;
        sc->handlercommand.command=0;
        SPIN_UNLOCK_IRQRESTORE(sc->handlercommand.lock, flags);
        /*pERROR(sc, "irq_thread: command=0x%x", command);*/

#ifdef IRQTHREAD_BRAKE
        brake--;
        if (brake<=0) {
        pERROR(sc, "irq_thread stopped by emergency brake, command=0x%x",
                command);
        command|=handlercomm_die;
    }
#endif

        sc->new_irqs=0;

        if (command&handlercomm_die) {
            pINFO(sc, "sis1100_irq_thread terminated");
#ifdef __NetBSD__
            SPIN_LOCK_IRQSAVE(sc->handlercommand.lock, flags);
            sc->handlercommand.command=0;
            wakeup(sc);
            SPIN_UNLOCK_IRQRESTORE(sc->handlercommand.lock, flags);
            kthread_exit(0);
#elif __linux__
            complete_and_exit(&sc->handler_completion, 0);
#endif
        }
        _sis1100_irq_thread(sc, command);

#ifdef __linux__
	if (signal_pending (current)) {
	    SPIN_LOCK_IRQSAVE(current->SIGMASK_LOCK, flags);
	    flush_signals(current);
	    SPIN_UNLOCK_IRQRESTORE(current->SIGMASK_LOCK, flags);
	}
#endif /*__linux__*/

    }
#ifdef __linux__
    return 0;
#endif
}
